/*  ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** 
*   Copyright 2014 Jan-Marwin Wollschläger                                   *
*                                                                            *
*   Licensed under the Apache License, Version 2.0 (the "License");          *
*   you may not use this file except in compliance with the License.         *
*   You may obtain a copy of the License at                                  *
*                                                                            *
*       http://www.apache.org/licenses/LICENSE-2.0                           *
*                                                                            *
*   Unless required by applicable law or agreed to in writing, software      *
*   distributed under the License is distributed on an "AS IS" BASIS,        *
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
*   See the License for the specific language governing permissions and      *
*   limitations under the License.                                           *
** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **   */   

import processing.core.*;
import java.util.ArrayList;


/**
 * The Default Controller for this application. According to the MVC pattern
 * this instance implements ("controlls") the interaction with the user, e.g.
 * mouse clicks/drags and key strokes must have effects on the model 
 * and perhaps also on the view. 
 *
 * @author Jan-Marwin Wollschläger
 * @version 0.1
 **/
class DefaultController implements IController
{
  private IModel model;
  private IView view;
  private PApplet parent;
  private UIMode uiMode;
  private String atomSymbol;
  private Pair previousClick;  // Needed for addition of bonds 
  
  private enum UIMode
  {
	  ADD_ATOMS,  // User wants to add atoms
	  ADD_BONDS,  // User wants to add single bonds
	  ADD_DOUBLE_BONDS,  // User wants to add double bonds
	  ADD_TRIPLE_BONDS
  }
  
  public DefaultController(IModel model, IView view, PApplet parent)
  {
    this.model = model; this.view = view;
    this.parent = parent;
    this.uiMode = UIMode.ADD_ATOMS;
    this.atomSymbol = "C";
  }
   
  /**
   * @param model The new model for this IController instance.
   **/
   public void setModel(IModel model)
   {
     this.model = model; 
   }
  
   /**
   * @param view The new view for this IController instance.
   **/
   public void setView(IView view)
   {
     this.view = view; 
   }
   
   /**
   * @param parent The PApplet that should be controlled by this instance, e.g.
   * this controller "listens" for user events on this instance and applies 
   * needed changes to the model.
   **/
   public void control(PApplet parent)
   {
     this.parent = parent; 
   }
   
   /**
    * Handle mouse clicks.
    **/
   public void mouseClicked()
   {
			final float mX = parent.mouseX;//20.0f;
			final float mY = parent.mouseY;
			
			if(parent.mouseButton == parent.RIGHT)
			{/* Show a menu/popup later ... */
				System.out.println("Right mouse click!");
			}
			
			/* User wants to add atoms to the model */
			if(uiMode == UIMode.ADD_ATOMS)
			{
				System.out.println("Adding atom!");
				/* Get the molecules of the current model */
				ArrayList<IMolecule> curMolecules = model.exposeModel();
				
				/* Create a new atom of the kind specified by the user */
				final IAtom      atomToAdd	    = new Atom(atomSymbol);
				
				/* Instantiate an empty molecule */
				final IMolecule moleculeToAdd = new Molecule();
				
				/* Set the position of the atom to mouse coordinates */
				atomToAdd.setPosition(new Vector2(mX, mY));
				
				/* Put the atom into a molecule, yielding a molecule
				* that contains only the single atom atomToAdd.  
				*/
				moleculeToAdd.addAtom(atomToAdd);
				
				/* Add the molecule to the list of molecules */
				curMolecules.add(moleculeToAdd);
				
				/* Update the model to the new molecule list */
				model.updateModel(curMolecules);
			}
			
			/* User wants to add single double or triple bonds to the model */
			if(uiMode == UIMode.ADD_BONDS || uiMode == UIMode.ADD_DOUBLE_BONDS
			|| uiMode == UIMode.ADD_TRIPLE_BONDS)
			{
				final int bondOrder;
				if(uiMode == UIMode.ADD_BONDS)
					bondOrder = 1;
				else
				if(uiMode == UIMode.ADD_DOUBLE_BONDS)
					bondOrder = 2;
				else/* Can only be triple bond */
					bondOrder = 3;
					
				System.out.println("Adding bond with order: "+bondOrder);
				
				/* If this is the first click during this mode, save
				*  it as previousClick pair of the mouse coordinates. 
				*/
				if(previousClick == null)
				{
					previousClick = new Pair((int)mX, (int)mY);
				}
				else
				{// So previousClick not null
				 //   
				    /* So their was a previous click already, which
				    * means it is now possible two add a new single bond
				    * according to the mouse (click) coordinates of the previous
				    * click and the current mouse (click) coordinates. 
				    */
				    
				    /* Get the nearest atom for the previous mouse click and
				    * the current mouse click.
				    */
					IAtom fstAtm 	= model.getAtomForClick(previousClick);
					IAtom sndAtm 	= model.getAtomForClick(new Pair((int)mX, (int)mY));
					
					/* After finding the atoms nearest two the two mouse clicks,
					* it is now essential to get their corresponding IMolecule instances. 
					*/
					IMolecule fstMol 	= model.getMoleculeForAtom(fstAtm);
					IMolecule sndMol 	= model.getMoleculeForAtom(sndAtm);
					
					/* Taking these two IMolecule instances, it is now possible to join
					* the two IMolecule instances together, finally joining both molecules
					* at the both atoms fstAtm in the first molecule and sndAtm in the
					* second molecule.  
					*/
					IMolecule joinedMolecule = fstMol.joinWithOrder(
														 fstAtm, 
														 sndAtm,
														 fstMol,
														 sndMol,
														 bondOrder);
					
					/* As now the joined molecule joinedMolecule combines the atoms and bonds
					* of the two connecting molecules fstMol and sndMol, the "old" molecules
					* fstMol and sndMol can now be removed. 
					*/
					boolean removedFirst  = model.removeMolecule(fstMol);  
					boolean removedSecond = model.removeMolecule(sndMol);  
					
					
					assert(removedFirst);  // TODO: assertion error thrown
					//assert(removedSecond); // when user accidentally 
											// adds bond to atom itself
											// prevent this!
											
					/* Finally add the joined molecule to the model. */
					model.addMolecule(joinedMolecule);  
					
				
					previousClick = null;  // Finally reset previous click	
				}
			}

   }
   
   /**
   * Simple map from an integer to an UIMode enum instance. 
   **/
   private UIMode uiModeFromInteger(int i)
   {
	   if(i <= 0)
			return UIMode.ADD_ATOMS;
	   else
	   if(i == 1)
			return UIMode.ADD_BONDS; 
	   else
	   if(i == 2)
			return UIMode.ADD_DOUBLE_BONDS;
	   else/* So i >= 3 */
			return UIMode.ADD_TRIPLE_BONDS; 
   }
  
  /**
  * Inverse of uiModeFromInteger: This method is a simple map from an
  * UIMode enum instance to an integer. 
  **/
  private int integerFromUIMode(UIMode mode)
  {
	  if(mode == UIMode.ADD_ATOMS)
		return 0;
	  if(mode == UIMode.ADD_BONDS)
		return 1;
	  else
	  if(mode == UIMode.ADD_DOUBLE_BONDS)
		return 2;
	  else/* Must be ADD_TRIPLE_BOND UIMode */
	    return 3;
  }
   
   /**
   * Handle pressed keys that were redirected to this instance
   **/
   public void keyPressed()
   {
	   /* User wants to switch mode 'upwards' */
	   if(parent.keyCode == parent.UP)
	   {
		   uiMode = uiModeFromInteger(integerFromUIMode(uiMode)+1);
	   }
	   
	   /* User wants to switch mode 'downwards' */
	   if(parent.keyCode == parent.DOWN)
	   {
		   uiMode = uiModeFromInteger(integerFromUIMode(uiMode)-1);
	   }
   }
}

